/**
 * @author Nicolas Latorre
 */
package sa4.form

import scala.collection.mutable.HashMap
import scala.xml._
import javax.servlet.http._
import sa4.routing.Routing
import sa4.Framework

class Form(val framework: Framework, val tags: List[Tag]) {
  type HandlerFunction = (HttpServletRequest, HttpServletResponse) => Unit
  def this(framework: Framework) = this(framework, List())

  /**
   * Function that generate a new form starting from the tag list.
   * @return	a xml.Elem that represent the new form.
   */
  def makeForm = {
    var attr = ""
    tags.head.attributes.map(x => attr += " " + x._1 + "=\"" + x._2 + "\"")
    var html = "<form " + attr + ">"
    for (tag <- tags.tail) {
      html += "<p><span>" + tag.span + "</span>"
      html += tag.makeNode
      html += "</p>"
      //if (tag.newline) html += "<br/>"
    }
    html += "</form>"
    XML.loadString(html)
  }

  /**
   * Function that will return the value of a particular attribute.
   * @param attribute		a String that represent the attribute in which we are interessed.
   */
  def getAttribute(attribute: String): String = {
    for (at <- tags.head.attributes) {
      at._1 match {
        case attribute => return at._2
      }
    }
    return ""
  }

  /**
   * Function that parse a template and install in the route table a new route with his handler function.
   * @param template a NodeSeq that represent the template of the form we want to process.
   * @return 	a NodeSeq that contain the template changed.
   */
  def prepareForm(template: NodeSeq, routing: Routing): NodeSeq = {
    import scala.xml.transform._
    import scala.xml.Null // hide scala.Null
    object MakeForm extends RewriteRule {
      override def transform(n: Node): Seq[Node] = n match {
        case form: Elem if form.label == "form" =>
          val Some(handler) = form.attribute("handler")
          var Some(method) = form.attribute("method")

          // Make the handler and install in routeTable
          val (uri, f: HandlerFunction) = makeHandler(handler.text)

          routing.addRoute(uri, f)

          // Add an action to the form.
          form % Attribute(None, "action", Text(uri), Null)
        case n => n
      }
    }
    template.map(ns => new RuleTransformer(MakeForm)(ns))
  }

  /**
   * Function that parse a header method and creates a new route and install it in the route table with its corresponding handler function.
   * @param s 	a String that represent the header of the method.
   */
  def makeHandler(s: String): (String, HandlerFunction) = {
    // Parse the handler string.
    val q = "^(\\w+)\\((.*)\\)$".r

    val (methodName, className) = s match {
      case q(methodName, className) => (methodName, className)
      case _ => throw new RuntimeException("bad handler: " + s)
    }

    val appClass = framework.getClass  /// Class.forName(className)
    val method = appClass.getMethod(methodName, classOf[HttpServletRequest], classOf[HttpServletResponse])

    val h = (req: HttpServletRequest, resp: HttpServletResponse) => {
      val result = method.invoke(framework, req, resp)
    }
    (handlerURI(methodName), h)
  }

  /**
   * Function that constructs a new route with the method name.
   * @param methodName 	a String that contain the method that will be used as path.
   */
  def handlerURI(methodName: String) = {
    "/" + methodName
  }
}

class Tag(val tag: String, val attributes: List[(String, String)], val span: String) {

  /**
   * Function that will generate an xml element starting form a string representing a tag and his attributes.
   * @return	a xml.Elem that represent a new tag.
   */
  def makeNode = {
    var attris = ""
    for (attribute <- attributes.map(x => " " + x._1 + "=\"" + x._2 + "\"")) {
      attris += attribute
    }
    XML.loadString("<" + tag + attris + " />")
  }
}